using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.Configuration;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net.Mail;
using System.Net.Mime;
using System.Linq;
using System.Data.Linq.Mapping;
using FlickrNet;

namespace ClubWebsite
{
    public static class Functions
    {
        public static MainDataClassesDataContext GetDatabaseDataContext()
        {
            return new MainDataClassesDataContext(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
        }

        /// <summary>
        /// Gets the column size.
        /// </summary>
        /// <returns>If the size is MAX or any other non-number, int.MaxValue is returned.</returns>
        /// <exception cref="ApplicationException">Thrown when the size cannot be found.</exception>
        public static int GetColumnSize(ColumnAttribute columnAttribute)
        {
            Match sizeMatch = Regex.Match(columnAttribute.DbType, @"(?<=\().+(?=\))");
            if (sizeMatch.Success)
            {
                int size = 0;
                if (int.TryParse(sizeMatch.Value, out size))
                {
                    return size;
                }
                else
                {
                    return int.MaxValue;
                }
            }
            else
            {
                throw new ApplicationException("Column size cannot be found.");
            }
        }

        /// <summary>
        /// Formats the given date/time into a full date/time string with either the full
        /// month or just the first 3 letters displayed.
        /// </summary>
        public static string FormatDateText(DateTime dateTime, bool shortMonth)
        {
            string dateText;
            if (shortMonth)
            {
                dateText = dateTime.ToString("dddd, dd MMM yyyy");
            }
            else
            {
                dateText = dateTime.ToString("dddd, dd MMMM yyyy");
            }
            if (dateTime.TimeOfDay.TotalSeconds > 0)
            {
                dateText += " " + dateTime.ToString("hh").TrimStart('0');
                if ((dateTime.Minute > 0) || (dateTime.Second > 0))
                {
                    dateText += ":" + dateTime.ToString("mm");
                    if (dateTime.Second > 0)
                    {
                        dateText += ":" + dateTime.ToString("ss");
                    }
                }
                dateText += " " + dateTime.ToString("tt");
            }
            return dateText;
        }

        /// <summary>
        /// Converts the given text into HTML and checks for special tags,
        /// converting them into their respective HTML
        /// </summary>
        public static string ConvertTextWithTagsIntoHTML(string textToConvert, MasterPage masterPage)
        {
            // Club tag
            Match match = null;
            do
            {
                match = Regex.Match(textToConvert, @"\[club=(?<id>((.|\n)*?))\](?<text>((.|\n)*?))\[\/club\]", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    // Find the unique URL to use
                    using (MainDataClassesDataContext databaseDataContext = Functions.GetDatabaseDataContext())
                    {
                        Guid clubID = new Guid(match.Groups["id"].Value);
                        string domain = databaseDataContext
                            .DBClubs
                            .Where(c => c.ID == clubID)
                            .Select(c => c.Domain)
                            .FirstOrDefault();
                        string url = "";
                        if ((domain != null) && (domain.Trim().Length > 0) && (domain.Trim() != HttpContext.Current.Request.Url.Host.Replace("www.", "")))
                        {
                            url = "http://www." + domain;
                        }
                        else
                        {
                            Guid domainClubID = databaseDataContext
                                .DBClubs
                                .Where(c => c.Domain.ToLower() == HttpContext.Current.Request.Url.Host.Replace("www.", "").ToLower())
                                .Select(c => c.ID)
                                .FirstOrDefault();
                            if (domainClubID != clubID)
                            {
                                url = "/" + databaseDataContext
                                    .DBClubs
                                    .Where(c => c.ID == clubID)
                                    .Select(c => c.UniqueTextForURLs)
                                    .Single();
                            }
                            url += "/" + databaseDataContext
                                .DBClubs
                                .Where(c => c.ID == clubID)
                                .Select(c => c.DBPage.UniqueTextForURLs)
                                .Single();
                        }
                        textToConvert = textToConvert.Remove(match.Index, match.Length);
                        textToConvert = textToConvert.Insert(match.Index, "<a href=\"" + url + "/\">" + match.Groups["text"].Value + "</a>");
                    }
                }
            }
            while (match.Success);

            // Page tag
            match = null;
            do
            {
                match = Regex.Match(textToConvert, @"\[page=(?<id>[0-9a-fA-F\-]{36})\](?<text>((.|\n)*?))\[\/page\]", RegexOptions.IgnoreCase);
                if (match.Success)
                {
                    // Find the unique URL to use
                    Guid pageID = new Guid(match.Groups["id"].Value);
                    string url = Functions.GetDatabaseDataContext()
                        .DBPages
                        .Where(p => p.ID == pageID)
                        .Select(p => p.UniqueTextForURLs)
                        .FirstOrDefault();
                    if (url == null)
                    {
                        // The page has not been created yet. Use the whole guid so that it can be gone too
                        url = pageID.ToString().Replace("-", "");
                    }
                    textToConvert = textToConvert.Remove(match.Index, match.Length);
                    textToConvert = textToConvert.Insert(match.Index, "<a href=\"../" + url + "/\">" + match.Groups["text"].Value + "</a>");
                }
            }
            while (match.Success);

            // Club settings tag
            if (UserHasPermissionToEditSection(SectionType.Club, ((Default)masterPage).ClubID))
            {
                textToConvert = Regex.Replace(textToConvert, @"\[clubsettings\](?<text>(.|\n)+?)\[\/clubsettings\]", @"<a href=""clubsettings.aspx"">${text}</a>", RegexOptions.IgnoreCase);
            }
            else
            {
                textToConvert = Regex.Replace(textToConvert, @"\[clubsettings\](?<text>(.|\n)+?)\[\/clubsettings\]", "", RegexOptions.IgnoreCase);
            }

            // Add menu item tag
            if (UserHasPermissionToEditSection(SectionType.Club, ((Default)masterPage).ClubID))
            {
                textToConvert = Regex.Replace(textToConvert, @"\[addmenuitem\](?<text>(.|\n)+?)\[\/addmenuitem\]", @"<a href=""clubsettings.aspx#menuitems"">${text}</a>", RegexOptions.IgnoreCase);
            }
            else
            {
                textToConvert = Regex.Replace(textToConvert, @"\[addmenuitem\](?<text>(.|\n)+?)\[\/addmenuitem\]", "", RegexOptions.IgnoreCase);
            }

            // Image tag
            textToConvert = Regex.Replace(textToConvert, @"\[image=(?<path>((.|\n)+?))\](?<text>((.|\n)*?))\[\/image\]", @"<img src=""/Files/${path}"" alt=""${text}"" />", RegexOptions.IgnoreCase);

            // List tags
            textToConvert = Regex.Replace(textToConvert, @"\[list](?<text>((.|\n)+?))\[\/list\]", @"<ul>${text}</ul>", RegexOptions.IgnoreCase);
            textToConvert = Regex.Replace(textToConvert, @"\[listitem]\[\/listitem\]", @"", RegexOptions.IgnoreCase);
            textToConvert = Regex.Replace(textToConvert, @"\[listitem](?<text>((.|\n)+?))\[\/listitem\]", @"<li>${text}</li>", RegexOptions.IgnoreCase);

            // Link tag
            textToConvert = Regex.Replace(textToConvert, @"\[link=(?<url>((.|\n)+?))\](?<text>((.|\n)*?))\[\/link\]", @"<a href=""${url}"">${text}</a>", RegexOptions.IgnoreCase);

            // Open URLs outside this site in a new tab/window
            textToConvert = Regex.Replace(textToConvert, @"<a href=""(?<path>http((.|\n)+?))""", @"<a href=""${path}"" target=""_blank""");

            // Page title tag
            if (Regex.IsMatch(textToConvert, @"\[pagetitle\]", RegexOptions.IgnoreCase))
            {
                textToConvert = Regex.Replace(textToConvert, @"\[pagetitle\]", "<span class=\"pagetitle\">" + ((Default)masterPage).PageTitle + "</span>", RegexOptions.IgnoreCase);
            }

            // YouTube tag
            string uniqueName = Guid.NewGuid().ToString().Replace("-", "");
            textToConvert = Regex.Replace(textToConvert, @"\[youtube\svideo=&quot;?(?<video>((.|\n)+?))&quot;?\]", String.Format(@"<script type=""text/javascript"">var embed_{0} = new SWFObject(""http://www.youtube.com/v/${{video}}&amp;autoplay=true"", ""1"", ""425"", ""350"", ""7"", ""#FFFFFF"");embed_{0}.addParam(""wmode"", ""transparent"");</script><div class=""videoimage""><a href=""http://www.youtube.com/watch?v=${{video}}"" onclick=""return hs.htmlExpand(this, {{ swfObject: embed_{0}, contentId: 'highslide-html_youtube' }} )"" class=""highslide""><img class=""photo"" src=""http://img.youtube.com/vi/${{video}}/2.jpg"" alt=""Video"" title=""Click to watch the video""/><span class=""videoimageoverlay""></span></a></div>", uniqueName), RegexOptions.IgnoreCase);

            // Remove any emtpy tags
            textToConvert = Regex.Replace(textToConvert, @"\[[^\]]+\]\s*\[\/[^\]]+\]", "", RegexOptions.IgnoreCase);

            return textToConvert;
        }

        /// <summary>
        /// Converts the given text into literal controls and checks for special tags,
        /// converting them into their respective controls
        /// </summary>
        public static void ConvertTextWithTagsIntoControls(string textToConvert, PlaceHolder placeHolder, System.Web.UI.Page page, bool ignorePostbackCheck)
        {
            Match firstRegexMatch;
            do
            {
                firstRegexMatch = null;
                Control control = null;

                // Edit page option tag
                Match regexMatch = Regex.Match(textToConvert, @"\[editpageoption\]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        /*
                        if (UserHasPermissionToEditSection(SectionType.Page, 
                        if ((HttpContext.Current.User.Identity.IsAuthenticated) && (((Default)page.Master).PageID != Guid.Empty))
                        {
                            control = (EditPageOption)page.LoadControl("EditPageOption.ascx");
                            ((EditPageOption)control).SaveChangesClicked += new EditPageOption.SaveChangesClickedEventHandler(((Default)page.Master).EditPageOption_SaveChangesClicked);
                        }*/
                    }
                }

                // Entry tag
                regexMatch = Regex.Match(textToConvert, @"\[entry=(?<id>((.|\n)*?))\]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (PageEntry)page.LoadControl("PageEntry.ascx");
                        if ((!page.IsPostBack) || (ignorePostbackCheck))
                        {
                            ((PageEntry)control).EntryID = new Guid(regexMatch.Groups["id"].Value);
                            if (Functions.UserHasPermissionToEditSection(Functions.SectionType.Entry, ((PageEntry)control).EntryID))
                            {
                                ((PageEntry)control).CurrentMode = PageEntry.Mode.CanEdit;
                            }
                            else
                            {
                                ((PageEntry)control).CurrentMode = PageEntry.Mode.View;
                            }
                        }
                    }
                }

                // Points table tag (league)
                regexMatch = Regex.Match(textToConvert, @"\[pointstable\sleagueid=(?<leagueid>((.|\n)*?))\]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (PointsTable)page.LoadControl("PointsTable.ascx");
                        if ((!page.IsPostBack) || (ignorePostbackCheck))
                        {
                            Guid leagueID = new Guid(regexMatch.Groups["leagueid"].Value);
                            PointsTable.TeamInfo[] teamInfos = Functions.GetDatabaseDataContext()
                                .LeaguesTeams
                                .Where(lt => lt.LeagueID == leagueID)
                                .Select(lt => new PointsTable.TeamInfo(lt.TeamID, lt.Team.Description, lt.DisplayOrder ?? 1000))
                                .ToArray();
                            ((PointsTable)control).TeamInfos = teamInfos;
                            ((PointsTable)control).IgnorePostbackCheck = ignorePostbackCheck;
                        }
                    }
                }

                // Points table tag (club teams)
                regexMatch = Regex.Match(textToConvert, @"\[pointstable\sclubid=(?<clubid>((.|\n)*?))\]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (PointsTable)page.LoadControl("PointsTable.ascx");
                        if ((!page.IsPostBack) || (ignorePostbackCheck))
                        {
                            Guid clubID = new Guid(regexMatch.Groups["clubid"].Value);
                            using (MainDataClassesDataContext databaseDataContext = Functions.GetDatabaseDataContext())
                            {
                                var clubTeams = databaseDataContext
                                   .ClubsTeams
                                   .Where(ct => ct.ClubID == clubID && ct.Current != 0)
                                   .Select(ct => new { ct.TeamID, ct.Alias, ct.Team.LeaguesTeams.First().Points, ct.Team.LeaguesTeams.First().MatchesPlayed, ct.Team.LeaguesTeams.First().Rank })
                                   .ToArray();
                                PointsTable.TeamInfo[] teamInfos = clubTeams
                                   .OrderBy(ct => int.Parse(Regex.Match(ct.Rank ?? "1000", @"\d+").Value))
                                   .ThenByDescending(ct => ct.Points)
                                   .ThenByDescending(ct => ct.MatchesPlayed)
                                   .ThenBy(ct => ct.Alias)
                                   .Select((ct, index) => new PointsTable.TeamInfo(ct.TeamID, ct.Alias, index))
                                   .ToArray();
                                ((PointsTable)control).TeamInfos = teamInfos;
                                ((PointsTable)control).Mode = PointsTable.DisplayMode.Narrow;
                                ((PointsTable)control).IgnorePostbackCheck = ignorePostbackCheck;
                            }
                        }
                    }
                }

                // Match list tag
                regexMatch = Regex.Match(textToConvert, @"\[matches\steamid=(?<teamid>((.|\n)*?))\]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (Matches)page.LoadControl("Matches.ascx");
                        if ((!page.IsPostBack) || (ignorePostbackCheck))
                        {
                            ((Matches)control).TeamID = new Guid(regexMatch.Groups["teamid"].Value);
                            ((Matches)control).IgnorePostbackCheck = ignorePostbackCheck;
                        }
                    }
                }

                // Club list tag
                regexMatch = Regex.Match(textToConvert, @"\[clublist]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (ClubsList)page.LoadControl("ClubsList.ascx");
                    }
                }

                // Add club tag
                regexMatch = Regex.Match(textToConvert, @"\[addclub]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (AddClub)page.LoadControl("AddClub.ascx");
                    }
                }

                // Add user tag
                regexMatch = Regex.Match(textToConvert, @"\[adduser]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (AddUser)page.LoadControl("AddUser.ascx");
                    }
                }

                // User email confirmation entry tag
                regexMatch = Regex.Match(textToConvert, @"\[confirmregistration]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (UserConfirmRegistration)page.LoadControl("UserConfirmRegistration.ascx");
                    }
                }

                // Email user password tag
                regexMatch = Regex.Match(textToConvert, @"\[emailuserpassword]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (EmailUserPassword)page.LoadControl("EmailUserPassword.ascx");
                    }
                }

                // Photo gallery (flickr tag)
                regexMatch = Regex.Match(textToConvert, "\\[(?'type'flickr|picasa)\\stags=(?:\"|&quot;)(?'tags'.+?)(?:\"|&quot;)\\suser=(?:\"|&quot;)(?'user'.+?)(?:\"|&quot;)\\sgallery=(?:\"|&quot;)(?'gallery'.+?)(?:\"|&quot;)(\\ssize=(?:\"|&quot;)(?'size'.+?)(?:\"|&quot;))?\\]", RegexOptions.IgnoreCase);
                if (regexMatch.Success)
                {
                    if ((firstRegexMatch == null) || (regexMatch.Index < firstRegexMatch.Index))
                    {
                        firstRegexMatch = regexMatch;
                        control = (PhotoGallery)page.LoadControl("PhotoGallery.ascx");
                        if ((!page.IsPostBack) || (ignorePostbackCheck))
                        {
                            ((PhotoGallery)control).Type = regexMatch.Groups["type"].Value;
                            ((PhotoGallery)control).Tags = regexMatch.Groups["tags"].Value;
                            ((PhotoGallery)control).User = regexMatch.Groups["user"].Value;
                            ((PhotoGallery)control).IgnorePostbackCheck = ignorePostbackCheck;
                            if ((regexMatch.Groups["Gallery"].Success) && (regexMatch.Groups["gallery"].Value.Trim().Length > 0))
                            {
                                ((PhotoGallery)control).User = regexMatch.Groups["gallery"].Value;
                            }
                        }
                    }
                }

                // Remove the tag and add the control to the PlaceHolder object (if applicable)
                if ((firstRegexMatch != null) && (firstRegexMatch.Success) && (firstRegexMatch.Index < textToConvert.Length))
                {
                    LiteralControl literalControl = new LiteralControl(textToConvert.Remove(firstRegexMatch.Index));
                    textToConvert = textToConvert.Remove(0, firstRegexMatch.Index + firstRegexMatch.Length);
                    placeHolder.Controls.Add(literalControl);
                    if (control != null)
                    {
                        placeHolder.Controls.Add(control);
                    }
                }
            }
            while ((firstRegexMatch != null) && (firstRegexMatch.Success));

            placeHolder.Controls.Add(new LiteralControl(textToConvert));
        }

        public enum SectionType
        {
            Club,
            ClubNewPage,
            Page,
            Entry
        }

        /// <summary>
        /// Checks if the current user is logged in and allowed to access the given section.
        /// </summary>
        /// <param name="sectionType">The section to check</param>
        /// <param name="itemID">The record ID of the section to check</param>
        /// <returns>True if the user is allowed to edit. False is they are not allowed.</returns>
        /// <summary>
        public static bool UserHasPermissionToEditSection(SectionType sectionType, Guid itemID)
        {
            if (HttpContext.Current.User.Identity.IsAuthenticated)
            {
                // Get the user ID
                Guid userID = new Guid(HttpContext.Current.User.Identity.Name);

                // Check the user's security against the section
                Guid clubID = Guid.Empty;
                using (MainDataClassesDataContext databaseDataContext = Functions.GetDatabaseDataContext())
                {
                    switch (sectionType)
                    {
                        case SectionType.Entry:
                            clubID = databaseDataContext
                                .Entries
                                .Where(e => e.ID == itemID)
                                .Select(e => e.ClubID)
                                .FirstOrDefault();
                            break;

                        case SectionType.Page:
                            clubID = databaseDataContext
                                .DBPages
                                .Where(p => p.ID == itemID)
                                .Select(p => p.ClubID)
                                .FirstOrDefault();
                            break;

                        case SectionType.ClubNewPage:
                        case SectionType.Club:
                            clubID = itemID;
                            break;
                    }
                    if (clubID != Guid.Empty)
                    {
                        bool userIsAllowedToModifyClub = databaseDataContext
                            .UsersClubs
                            .Any(uc => (uc.ClubID == clubID) && (uc.UserID == userID));
                        return userIsAllowedToModifyClub;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Checks for any invalid input and converts tags where necessary
        /// </summary>
        /// <param name="text">The user's input</param>
        /// <returns>Safe text for saving to the database</returns>
        public static string ConvertUserTextToSafeText(string text)
        {
            // Convert new page tags into a normal page tag
            Match match = Regex.Match(text, @"(?<=\[page=)new(?=\]((.|\n)+?)\[\/page\])", RegexOptions.IgnoreCase);
            while (match.Success)
            {
                text = text.Remove(match.Index, match.Length);
                text = text.Insert(match.Index, Guid.NewGuid().ToString());
                match = Regex.Match(text, @"(?<=\[page=)new(?=\]((.|\n)+?)\[\/page\])", RegexOptions.IgnoreCase);
            };

            // Return the text
            return text;
        }

        /// <summary>
        /// Logs the given message to the database
        /// </summary>
        /// <param name="message">The message to log</param>
        /// <param name="isError">Specifies if this is an error log, instead of an info log</param>
        /// <param name="exception">The exception that caused the error, if applicable</param>
        public static void WriteLogEntry(string message, bool isError, Exception exception)
        {
            // Attempt to write to the database log 
            try
            {
                if ((exception is ApplicationException) && (exception.Message.StartsWith("Test log file")))
                {
                    throw exception;
                }

                using (SqlCommand cmd = new SqlCommand("INSERT INTO [Logs] ([UTC], [IsError], [Message]) VALUES (GETUTCDATE(), @IsError, @Message)", new SqlConnection(WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString)))
                {
                    cmd.Parameters.AddWithValue("@IsError", isError);
                    if (message != null)
                    {
                        cmd.Parameters.AddWithValue("@Message", message);
                    }
                    else
                    {
                        cmd.Parameters.AddWithValue("@Message", "No message.");
                    }
                    if (exception != null)
                    {
                        StringBuilder exceptionLog = new StringBuilder();
                        exceptionLog.AppendLine("Exception:\t" + exception.Message);
                        exceptionLog.AppendLine("Expection Stack Trace:" + exception.StackTrace);
                        Exception innerException = exception.InnerException;
                        while (innerException != null)
                        {
                            exceptionLog.AppendLine("Inner Exception:\t" + innerException.Message);
                            exceptionLog.AppendLine("Inner Expection Stack Trace:\t" + innerException.StackTrace);
                            innerException = innerException.InnerException;
                        }
                        cmd.Parameters["@Message"].Value += "\r\n" + exceptionLog.ToString();
                    }
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                // Exception while trying to write the log to the database. Apptempt to write the log to a file
                WriteLogEntryToTextLog(message, exception, ex);
            }
        }

        /// <summary>
        /// Writes a log file for the given message and exception
        /// </summary>
        private static void WriteLogEntryToTextLog(string message, Exception exception, Exception databaseLogException)
        {
            // An error occoured while attempting to write to the database log, use the text log

            // Set the name of the log file
            string logFolder = System.Web.Hosting.HostingEnvironment.MapPath(@"~/Files/Logs/");
            DateTime logDateTime = DateTime.UtcNow;
            string logFileName = "Log " + logDateTime.ToString("yyyyMMddHHmmss");

            // Attempt to write the log file
            bool canWrite = true;
            int writeAttemptCount = 0;
            do
            {
                writeAttemptCount++;
                try
                {
                    if ((databaseLogException is ApplicationException) && (databaseLogException.Message == "Test log file 2"))
                    {
                        throw databaseLogException;
                    }

                    using (StreamWriter sw = new StreamWriter(System.IO.Path.Combine(logFolder, logFileName + " " + writeAttemptCount + ".txt")))
                    {
                        if ((exception.GetType() != typeof(ApplicationException)) || (exception.Message != "Test log file"))
                        {
                            sw.WriteLine("Date Time:\t" + logDateTime.ToString("dd MMMM yyyy, hh:mm:ss tt"));
                            sw.WriteLine("Message:\t" + message);
                            if (exception != null)
                            {
                                sw.WriteLine("Exception:\t" + exception.Message);
                                sw.WriteLine("Expection Stack Trace:" + exception.StackTrace);
                                Exception innerException = exception.InnerException;
                                while (innerException != null)
                                {
                                    sw.WriteLine("Inner Exception:\t" + innerException.Message);
                                    sw.WriteLine("Inner Expection Stack Trace:\t" + innerException.StackTrace);
                                    innerException = innerException.InnerException;
                                }
                            }
                            if (databaseLogException != null)
                            {
                                sw.WriteLine("Database Exception:\t" + databaseLogException.Message);
                                sw.WriteLine("Database Expection Stack Trace:" + databaseLogException.StackTrace);
                                Exception innerException = databaseLogException.InnerException;
                                while (innerException != null)
                                {
                                    sw.WriteLine("Database Inner Exception:\t" + innerException.Message);
                                    sw.WriteLine("Database Inner Expection Stack Trace:\t" + innerException.StackTrace);
                                    innerException = innerException.InnerException;
                                }
                            }
                        }
                        else
                        {
                            sw.WriteLine("Log test successful");
                        }
                    }
                }
                catch
                {
                    // Not much can be done since this failed
                }
            }
            while ((!canWrite) && (writeAttemptCount < 20));
        }

        /// <summary>
        /// Gets the current database connnection string
        /// </summary>
        private static string GetConnectionString()
        {
            return ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
        }

        /// <summary>
        /// Recalcaulates the stats for the given league
        /// </summary>
        public static void UpdateLeagueStatistics(Guid leagueID)
        {
            using (MainDataClassesDataContext databaseDataContext = Functions.GetDatabaseDataContext())
            {
                var leagueProperties = databaseDataContext
                    .Leagues
                    .Where(l => l.ID == leagueID)
                    .Select(l => new { l.WinPoints, l.DrawPoints, l.LosePoints, l.ByeCountsAsWin, l.ByeScoreFor })
                    .Single();

                // Get the points awarded for wins, draws and losses
                int winPoints = leagueProperties.WinPoints;
                int drawPoints = leagueProperties.DrawPoints;
                int losePoints = leagueProperties.LosePoints;

                // Find out if byes should count as wins
                bool byesCountAsWin = leagueProperties.ByeCountsAsWin != 0 ? true : false;
                int byeScoreFor = 0;
                if (byesCountAsWin)
                {
                    byeScoreFor = leagueProperties.ByeScoreFor;
                }

                // Update the statistic data for the teams in the division
                using (SqlConnection sqlConnection = new SqlConnection(GetConnectionString()))
                {
                    using (SqlCommand updateStatistics = new SqlCommand())
                    {
                        updateStatistics.Connection = sqlConnection;
                        updateStatistics.CommandText =
                          "UPDATE [LeaguesTeams] SET"
                          + " [MatchesPlayed] = (SELECT COUNT(*) FROM [Matches] WHERE ((([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([UTC] < GETUTCDATE())) OR (([UTC] < GETUTCDATE()) AND ([VisitorTeamID] = [LeaguesTeams].[TeamID]))) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND (ISNULL([Bye], 0) = 0) AND ([LeagueID] = @LeagueID))"
                          + ", [MatchesWon] = (SELECT COUNT(*) FROM [Matches] WHERE ((([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([HomeScore] > [VisitorScore])) OR (([VisitorTeamID] = [LeaguesTeams].[TeamID]) AND ([VisitorScore] > [HomeScore]))) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID))"
                          + ", [MatchesDrawn] = (SELECT COUNT(*) FROM [Matches] WHERE ((([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([HomeScore] = [VisitorScore])) OR (([VisitorTeamID] = [LeaguesTeams].[TeamID]) AND ([VisitorScore] = [HomeScore]))) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID))"
                          + ", [MatchesLost] = (SELECT COUNT(*) FROM [Matches] WHERE ((([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([HomeScore] < [VisitorScore])) OR (([VisitorTeamID] = [LeaguesTeams].[TeamID]) AND ([VisitorScore] < [HomeScore]))) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID))"
                          + ", [ScoreFor] = ISNULL((SELECT SUM(ISNULL([HomeScore], 0)) FROM [Matches] WHERE ([HomeTeamID] = [LeaguesTeams].[TeamID]) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID)), 0) + ISNULL((SELECT SUM(ISNULL([VisitorScore], 0)) FROM [Matches] WHERE ([VisitorTeamID] = [LeaguesTeams].[TeamID]) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID)), 0)"
                          + ", [ScoreAgainst] = ISNULL((SELECT SUM(ISNULL([HomeScore], 0)) FROM [Matches] WHERE ([VisitorTeamID] = [LeaguesTeams].[TeamID]) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID)), 0) + ISNULL((SELECT SUM(ISNULL([VisitorScore], 0)) FROM [Matches] WHERE ([HomeTeamID] = [LeaguesTeams].[TeamID]) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID)), 0)"
                          + " WHERE [LeagueID] = @LeagueID";

                        // Include byes
                        if (byesCountAsWin)
                        {
                            updateStatistics.CommandText += "; UPDATE [LeaguesTeams] SET"
                            + " [MatchesWon] = [MatchesWon] + (SELECT COUNT(*) FROM [Matches] WHERE ([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([Bye] = 1) AND ([UTC] < GETUTCDATE()) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID))"
                            + " WHERE [LeagueID] = @LeagueID";
                            updateStatistics.CommandText += "; UPDATE [LeaguesTeams] SET"
                            + " [MatchesPlayed] = [MatchesPlayed] + (SELECT COUNT(*) FROM [Matches] WHERE ([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([Bye] = 1) AND ([UTC] < GETUTCDATE()) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID))"
                            + " WHERE [LeagueID] = @LeagueID";
                        }
                        if (byeScoreFor != 0)
                        {
                            updateStatistics.CommandText += "; UPDATE [LeaguesTeams] SET"
                            + " [ScoreFor] = [ScoreFor] + ((SELECT COUNT(*) FROM [Matches] WHERE ([HomeTeamID] = [LeaguesTeams].[TeamID]) AND ([Bye] = 1) AND ([UTC] < GETUTCDATE()) AND (ISNULL([Cancelled], 0) = 0) AND (ISNULL([Postponed], 0) = 0) AND ([LeagueID] = @LeagueID)) * @ByeScoreFor)"
                            + " WHERE [LeagueID] = @LeagueID";
                        }

                        // Calculate the total points and the total score difference
                        updateStatistics.CommandText += "; UPDATE [LeaguesTeams] SET"
                        + " [Points] = ([MatchesWon] * @WinPoints) + ([MatchesDrawn] * @DrawPoints) + ([MatchesLost] * @LosePoints)"
                        + ", [ScoreDifference] = [ScoreFor] - [ScoreAgainst]"
                        + " WHERE [LeagueID] = @LeagueID";

                        // Update the division's statistics
                        updateStatistics.Parameters.AddWithValue("@LeagueID", leagueID);
                        if (byeScoreFor != 0)
                        {
                            updateStatistics.Parameters.AddWithValue("@ByeScoreFor", byeScoreFor);
                        }
                        updateStatistics.Parameters.AddWithValue("@WinPoints", winPoints);
                        updateStatistics.Parameters.AddWithValue("@DrawPoints", drawPoints);
                        updateStatistics.Parameters.AddWithValue("@LosePoints", losePoints);
                        updateStatistics.Connection.Open();
                        updateStatistics.ExecuteNonQuery();
                        updateStatistics.Connection.Close();
                    }

                    // Calcaulte the display order and the ranking
                    using (SqlCommand selectStatistics = new SqlCommand())
                    {
                        selectStatistics.Connection = sqlConnection;
                        selectStatistics.CommandText = "SELECT [TeamID], [Points] FROM [LeaguesTeams] INNER JOIN [Teams] ON ([Teams].[ID] = [LeaguesTeams].[TeamID]) WHERE [LeagueID] = @LeagueID ORDER BY [LeaguesTeams].[Points] DESC, [ScoreDifference] DESC, [Teams].[Description]";
                        selectStatistics.Parameters.AddWithValue("@LeagueID", leagueID);
                        System.Text.StringBuilder updateOrderText = new System.Text.StringBuilder();
                        selectStatistics.Connection.Open();
                        using (SqlDataReader reader = selectStatistics.ExecuteReader())
                        {
                            if (reader.HasRows)
                            {
                                int currentOrder = 0;
                                int currentRank = 0;
                                int previousPoints = 0;
                                string updateOrderTextLine = "";
                                while (reader.Read())
                                {
                                    currentOrder++;

                                    if (updateOrderTextLine.Length > 0)
                                    {
                                        // Check if the current points are the previous team's points
                                        if (previousPoints == Convert.ToInt32(reader[1]))
                                        {
                                            // Points are the same
                                            updateOrderTextLine = updateOrderTextLine.Replace("@RankEqual", "=");
                                        }
                                        else
                                        {
                                            // Points are different
                                            updateOrderTextLine = updateOrderTextLine.Replace("@RankEqual", "");
                                            currentRank = currentOrder;
                                        }
                                        updateOrderText.Append(updateOrderTextLine);
                                    }
                                    else
                                    {
                                        currentRank = currentOrder;
                                    }

                                    // Set the command text for the current team
                                    updateOrderTextLine = "UPDATE [LeaguesTeams] SET [DisplayOrder] = @DisplayOrder, [Rank] = '@Rank' WHERE TeamID = @TeamID;\r\n";
                                    updateOrderTextLine = updateOrderTextLine.Replace("@DisplayOrder", currentOrder.ToString());
                                    switch (currentRank)
                                    {
                                        case 1:
                                            updateOrderTextLine = updateOrderTextLine.Replace("@Rank", "1st@RankEqual");
                                            break;

                                        case 2:
                                            updateOrderTextLine = updateOrderTextLine.Replace("@Rank", "2nd@RankEqual");
                                            break;

                                        case 3:
                                            updateOrderTextLine = updateOrderTextLine.Replace("@Rank", "3rd@RankEqual");
                                            break;

                                        default:
                                            updateOrderTextLine = updateOrderTextLine.Replace("@Rank", currentRank.ToString() + "th@RankEqual");
                                            break;
                                    }
                                    updateOrderTextLine = updateOrderTextLine.Replace("@TeamID", "'" + reader[0].ToString() + "'");

                                    // Check if the current points are the previous team's points
                                    if (previousPoints == int.Parse(reader[1].ToString()))
                                    {
                                        // Points are the same
                                        updateOrderTextLine = updateOrderTextLine.Replace("@RankEqual", "=");
                                    }

                                    previousPoints = int.Parse(reader[1].ToString());
                                }
                                // Complete the final team
                                updateOrderTextLine = updateOrderTextLine.Replace("@RankEqual", "");
                                updateOrderText.Append(updateOrderTextLine);
                            }
                        }
                        selectStatistics.Connection.Close();

                        // Update the display orders
                        if (updateOrderText.Length > 0)
                        {
                            using (SqlCommand updateOrder = new SqlCommand())
                            {
                                updateOrder.Connection = sqlConnection;
                                updateOrder.CommandText = updateOrderText.ToString();
                                updateOrder.Connection.Open();
                                updateOrder.ExecuteNonQuery();
                                updateOrder.Connection.Close();
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Checks if the current user is a site adminsitrator.
        /// </summary>
        /// <returns><c>True</c> if the user is a site administrator.</returns>
        public static bool UserIsSiteAdminstrator()
        {
            // Check if the user is logged in.
            if (HttpContext.Current == null
                || HttpContext.Current.User == null
                || HttpContext.Current.User.Identity == null
                || !HttpContext.Current.User.Identity.IsAuthenticated)
            {
                return false;
            }

            // Check if the user is in the database (should always be if they are logged in) and that they are a
            // site administrator.
            using (MainDataClassesDataContext databaseDataContext = Functions.GetDatabaseDataContext())
            {
                User user = databaseDataContext.Users
                    .Where(u => u.ID.ToString() == HttpContext.Current.User.Identity.Name)
                    .SingleOrDefault();
                if (user == null || user.SiteAdministrator == 0)
                {
                    return false;
                }
            }

            // All checks passed.
            return true;
        }
    }

    public class WebsiteEmail
    {
        public static void SendEmailToConfirmEmailAddress(string email, string emailConfirmationCode)
        {
            string to = email;
            string subject = "Welcome To Everyone's Club";
            string confirmationURL = "http://www.EveryonesClub.com/confirmregistration/Default.aspx?Email=" + HttpUtility.HtmlEncode(email) + "&ConfirmationCode=" + emailConfirmationCode;
            string message = "Great! Thanks for joining Everyone's Club."
                + "\n\r<br />"
                + "\n\r<br />"
                + "\n\r<br />Before you can log in you need to confirm your email address."
                + "\n\r<br />"
                + "\n\r<br />Please <a href=\"" + confirmationURL + "\">click here</a> to confirm your email address."
                + "\n\r<br />"
                + "\n\r<br />Alternatively, when you attempt to log in, you will be asked to enter the confirmation code. Your confirmation code is: " + emailConfirmationCode + "."
                + "\n\r<br />"
                + "\n\r<br />"
                + "\n\r<br />We hope you enjoy using our website.";
            SendEmail(to, subject, message, true);
        }

        public static void SendEmailWithPassword(string email, string password)
        {
            string to = email;
            string subject = "Everyone's Club Password";
            string message = "Hello, your password for Everyone's Club is: " + password + ".";
            SendEmail(to, subject, message, false);
        }

        public static void SendEmailToConfirmEmailAddressPlainText(string email, string emailConfirmationCode)
        {
            string to = email;
            string subject = "Everyone's Club Confirmation Code";
            string message = "Thanks for joining Everyone's Club. When you log in you'll be asked for a confirmation code. The code is: " + emailConfirmationCode + ".";
            SendEmail(to, subject, message, false);
        }

        private static void SendEmail(string to, string subject, string message, bool sendAsHTML)
        {
            // Create the message
            MailMessage mail = new MailMessage();
            mail.From = new MailAddress("noreply@everyonesclub.com", "Everyone's Club");
            mail.To.Add(to);
            mail.Subject = subject;
            mail.IsBodyHtml = sendAsHTML;
            mail.Body = message;

            // Send the message
            SmtpClient smtp = new SmtpClient("127.0.0.1");
            smtp.Send(mail);
        }

        /// <summary>
        /// Uses thew MailMessage object to check if the given email address is valid
        /// </summary>
        public static bool EmailAddressIsValid(string emailAddress)
        {
            using (MailMessage mail = new MailMessage())
            {
                try
                {
                    mail.To.Add(emailAddress);
                }
                catch
                {
                    return false;
                }
            }
            return true;
        }
    }

    public class RandomFunctions
    {
        /// <summary>
        /// Creates a random code
        /// </summary>
        public static string CreateRandomCode(int characterLength)
        {
            string code = "";
            Random random = new Random(GenerateSeed());
            for (int i = 0; i < characterLength; i++)
            {
                // Mix numbers and letters
                if (random.Next(1, 3) == 1)
                {
                    code += CreateRandomNumber(1, 9);
                }
                else
                {
                    code += CreateRandomLetter(false);
                }
            }
            return code;
        }

        public static string CreateRandomLetter(bool lowerCase)
        {
            string builder;
            Random random = new Random(GenerateSeed());
            char ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
            builder = ch.ToString();
            if (lowerCase)
            {
                return builder.ToLower();
            }
            else
            {
                return builder;
            }
        }

        public static string CreateRandomString(int size, bool lowerCase)
        {
            System.Text.StringBuilder builder = new System.Text.StringBuilder();
            Random random = new Random(GenerateSeed());
            char ch;
            for (int i = 0; i < size; i++)
            {
                ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                builder.Append(ch);
            }
            if (lowerCase)
            {
                return builder.ToString().ToLower();
            }
            else
            {
                return builder.ToString();
            }
        }

        public static int CreateRandomNumber(int min, int max)
        {
            Random random = new Random(GenerateSeed());
            return random.Next(min, max);
        }

        private static int GenerateSeed()
        {
            // Because we cannot use the default randomizer, which is based on the
            // current time (it will produce the same "random" number within a
            // second), we will use a random number generator to seed the
            // randomizer.

            // Use a 4-byte array to fill it with random bytes and convert it then
            // to an integer value.
            byte[] randomBytes = new byte[4];

            // Generate 4 random bytes.
            System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
            rng.GetBytes(randomBytes);

            // Convert 4 bytes into a 32-bit integer value.
            int seed = (randomBytes[0] & 0x7f) << 24 |
                        randomBytes[1] << 16 |
                        randomBytes[2] << 8 |
                        randomBytes[3];

            return seed;
        }
    }
}